using System;

namespace SharePoint.DataMapper
{
	public class Lookup<T> : Lookup<T, IItemBase>, IItemBase
		where T : class, IItemBase, new()
	{
		public Lookup(IItemBase baseValue, ListDataContext context)
			: base(baseValue)
		{
			Load = itemBase => context.GetList<T>().GetItem(itemBase.Id);
		}

		public Lookup(T value) : base(value)
		{
		}

		public static implicit operator Lookup<T>(T value)
		{
			return new Lookup<T>(value);
		}

		public static explicit operator T(Lookup<T> lookup)
		{
			return lookup.Value;
		}

		public Guid ListId
		{
			get { return BaseValue.ListId; }
			set { BaseValue.ListId = value; }
		}

		public int Id
		{
			get { return BaseValue.Id; }
			set { BaseValue.Id = value; }
		}

		public string Title
		{
			get { return BaseValue.Title; }
			set { BaseValue.Title = value; }
		}
	}

	public class Lookup<T,TBase>
		where T : TBase
	{
		private bool _loaded;
		private readonly TBase _baseValue;
		private T _value;

		public Func<TBase, T> Load { get; set; }

		public Lookup(TBase baseValue)
		{
			_baseValue = baseValue;
		}

		public Lookup(T value)
		{
			_value = value;
			_loaded = true;
		}

		public bool Loaded
		{
			get
			{
				return _loaded;
			}
		}

		public T Value
		{
			get
			{
				if (!Loaded)
				{
					if (Load != null)
					{
						_value = Load(_baseValue);
					}
					if (_value != null)
					{
						_loaded = true;
					}
				}
				return _value;
			}
		}

		public TBase BaseValue
		{
			get
			{
				if (!Loaded)
				{
					return _baseValue;
				}
				return _value;
			}
		}

		public override bool Equals(object other)
		{
			if (other == null)
			{
				return false;
			}
			if (!Loaded)
			{
				return _baseValue.Equals(other);
			}
			return _value.Equals(other);
		}

		public override int GetHashCode()
		{
			if (!Loaded)
			{
				return _baseValue.GetHashCode();
			}
			return _value.GetHashCode();
		}

		public override string ToString()
		{
			if (!Loaded)
			{
				return _baseValue.ToString();
			}
			return _value.ToString();
		}

		public static implicit operator Lookup<T,TBase>(T value)
		{
			return new Lookup<T,TBase>(value);
		}

		public static implicit operator Lookup<T,TBase>(TBase baseValue)
		{
			return new Lookup<T,TBase>(baseValue);
		}

		public static explicit operator T(Lookup<T,TBase> lookup)
		{
			return lookup.Value;
		}

		public static explicit operator TBase(Lookup<T,TBase> lookup)
		{
			return lookup.BaseValue;
		}
	}
}